package variabilitysmelldetection.analyses;

import java.util.ArrayList;
import java.util.List;

import variabilitysmelldetection.measurement.measures.Measure;
import variabilitysmelldetection.measurement.measures.component.NumberOfOperationOverrides;
import variabilitysmelldetection.measurement.measures.method.CyclomaticComplexity;
import variabilitysmelldetection.measurement.measures.method.MethodLinesOfCode;
import variabilitysmelldetection.measurement.measures.method.NumberOfMethodRefinements;
import variabilitysmelldetection.measurement.models.ComponentItem;
import variabilitysmelldetection.measurement.models.MethodItem;
import variabilitysmelldetection.measurement.models.Project;
import variabilitysmelldetection.plugin.Activator;
import variabilitysmelldetection.plugin.preferences.PreferenceConstants;


public class GodMethod {
	
	private double noorAndNmrThreshold;
	private double mLocThreshold;
	private double cycloThreshold;
	private String strategy;
	
	public GodMethod(){
		noorAndNmrThreshold = Activator.getDefault().getPreferenceStore().getDouble(PreferenceConstants.GODMETHOD_NOOR_NMR_THRESHOLD);//2.39;  // high
		mLocThreshold       = Activator.getDefault().getPreferenceStore().getDouble(PreferenceConstants.GODMETHOD_MLOC_THRESHOLD);//10.09; //avg
		cycloThreshold      = Activator.getDefault().getPreferenceStore().getDouble(PreferenceConstants.GODMETHOD_CYCLO_THRESHOLD);//0.24;  // high
		strategy = "((NOOr+NMR) > "+noorAndNmrThreshold+") OR "
				    + "((MLoC > "+mLocThreshold+") AND "
				    + "((Cyclo/MLoC) > "+cycloThreshold+"))";
	}
	
	public String getStrategy(){
		return strategy;
	}
	
	public boolean hasGodMethod(MethodItem method){
		double numOverrides = calculateOverrides(method);
		double mLoc         = calculateMLoC(method);
		double cyclo        = calculateCyclo(method);

		return ((numOverrides > noorAndNmrThreshold) ||
				((mLoc > mLocThreshold) &&
				(cyclo > cycloThreshold)));
	}
	
	private double calculateOverrides(MethodItem method){
		double value = 0d;
		Measure noor = method.getMeasure(NumberOfOperationOverrides.class);
		Measure nmr  = method.getMeasure(NumberOfMethodRefinements.class);
		if (noor != null && nmr != null) {
			value = noor.getValue() + nmr.getValue();
		}
		
		return value;
	}
	
	private double calculateMLoC(MethodItem method){
		double value = 0d;
		Measure mLoC = method.getMeasure(MethodLinesOfCode.class);
		if (mLoC != null) {
			value = mLoC.getValue();
		}
		
		return value;
	}
	
	private double calculateCyclo(MethodItem method){
		double value = 0d;
		Measure mLoC  = method.getMeasure(MethodLinesOfCode.class);
		Measure cyclo = method.getMeasure(CyclomaticComplexity.class);
		if (mLoC != null && cyclo != null) {
			value = cyclo.getValue() / mLoC.getValue();
		}
		
		return value;
	}

	public List<MethodItem> getGodMethods(Project project){
		List<MethodItem> godMethods = new ArrayList<MethodItem>();
		
		for (MethodItem method : getMethods(project)) {
			if (hasGodMethod(method)) {
				godMethods.add(method);
			}
		}
		return godMethods;
	}
	
	private List<MethodItem> getMethods(Project project){
		List<ComponentItem> classes = project.getClassItems();
		List<MethodItem> methods = new ArrayList<MethodItem>();
		for (ComponentItem classe : classes) {
			methods.addAll(classe.getMethods());
		}
		return methods;
	}
}
